package com.project.base.service.imppl;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.project.base.mapper.UserMapper;
import com.project.base.service.UserService;
import com.project.cache.helper.CacheProxy;
import com.project.core.auth.LoginDTO;
import com.project.core.auth.LoginRes;
import com.project.core.auth.UserDTO;
import com.project.core.config.TokenProperties;
import com.project.core.constant.DictConstant;
import com.project.core.constant.ScoreConstant;
import com.project.core.entity.User;
import com.project.core.util.AesUtil;
import com.project.core.util.CommonUtil;
import com.project.core.util.JwtUtil;
import com.project.core.util.ScoreContext;
import org.springframework.beans.BeanUtils;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.Objects;

/**
 * @author mengxj
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    private CacheProxy cache = new CacheProxy();

    @Resource
    private JwtUtil jwtUtil;

    @Resource
    private TokenProperties tokenProperties;

    @Override
    public LoginRes login(LoginDTO login) {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        String loginId = login.getLoginId();
        wrapper.eq(User::getLoginId, loginId);
        User user = super.getOne(wrapper);
        if(Objects.isNull(user)){
            CommonUtil.throwException("账号错误");
        }
        String aesPassword = user.getPassword();
        if(!aesPassword.equals(AesUtil.encrypt(login.getPassword()))){
            CommonUtil.throwException("密码错误");
        }
        LoginRes res = this.createNewToken(loginId, user);
        user.setOnline(DictConstant.TRUE);
        user.setLastLogin(LocalDateTime.now());
        super.updateById(user);
        return res;
    }

    private LoginRes createNewToken(String loginId, User user){
        LoginRes res = new LoginRes();
        res.setUser(user);
        String token = this.jwtUtil.generateToken(loginId);
        res.setToken(token);
        String key = LocalDateTime.now().toString();
        res.setTempKey(key);
        String refreshToken = AesUtil.encrypt(loginId, key);
        res.setRefreshToken(refreshToken);
        this.cache.remove(token);
        this.cache.remove(refreshToken);
        this.cache.set(token, res, this.tokenProperties.getTokenExpire());
        this.cache.set(refreshToken, res, this.tokenProperties.getRefreshToken());
        String[] tokens = {token, refreshToken};
        this.cache.remove(loginId);
        this.cache.set(loginId, tokens);
        return res;
    }

    @Override
    public LoginRes refreshToken(HttpServletRequest request) {
        Object obj = request.getHeader(ScoreConstant.REFRESH_TOKEN);
        if(Objects.isNull(obj)){
            CommonUtil.throwException(ScoreConstant.AUTH_ERROR_CODE, "原token失效，丢失refresh-token，无法刷新token！");
        }
        String refreshToken = obj.toString();
        LoginRes lastInfo = this.cache.get(refreshToken, LoginRes.class);
        if(Objects.isNull(lastInfo)){
            CommonUtil.throwException("登录超时，请重新登陆！");
        }
        User user = lastInfo.getUser();
        String loginId = null;
        try {
            loginId = AesUtil.decrypt(refreshToken, lastInfo.getTempKey());
        }catch (Exception e){
            CommonUtil.throwException(ScoreConstant.AUTH_ERROR_CODE, "解密失败，请检查refresh-token是否正确", e);
        }
        assert loginId != null;
        if(!loginId.equals(user.getLoginId())){
            CommonUtil.throwException("登录失败，请重新登陆！");
        }
        return createNewToken(loginId, user);
    }

    @Override
    public void save(UserDTO user) {
        Long id = user.getId();
        String loginId = user.getLoginId();
        try {
            User us = new User();
            BeanUtils.copyProperties(user, us);
            String password = user.getPassword();
            if(StrUtil.isEmpty(password)){
                password = ScoreConstant.DEFAULT_PASSWORD;
            }
            us.setPassword(AesUtil.encrypt(password));
            us.setRealPassword(password);
            if(Objects.isNull(id)){
                super.save(us);
            }else{
                super.updateById(us);
            }
        }catch (DuplicateKeyException e){
            String msg = String.format("登录账号 %s 已存在！", loginId);
            CommonUtil.throwException(msg, e);
        }
    }

    @Override
    public void logout() {
        User user = ScoreContext.currentUser();
        String loginId = user.getLoginId();
        String[] tokens = this.cache.get(loginId, String[].class);
        this.cache.remove(loginId);
        this.cache.remove(tokens[0]);
        this.cache.remove(tokens[1]);
        user.setOnline(DictConstant.FALSE);
        super.updateById(user);
    }
}
